using System.Collections.Generic;
using System.Linq;
using GorillaEngine.Core.Extensions;
using GorillaEngine.ICore;
using Microsoft.Xna.Framework;

namespace GorillaEngine.Core.Engine {
    public class PhysicsEngine : IPhysicsEngine {

        private readonly Vector2 _gravity;
        private readonly Vector2 _airResistance;
        
        private readonly List<IPhysicsObject> _physicElements;
        public List<IPhysicsObject> PhysicElements { get { return _physicElements; } }

        public PhysicsEngine() {
            _gravity = new Vector2(0,.981f);
            _airResistance = new Vector2(0.2f,0f);
            _physicElements = new List<IPhysicsObject>();
        }

        public PhysicsEngine(Vector2 gravity, Vector2 airResistance)
        {
            _gravity = gravity;
            _airResistance = airResistance;
            _physicElements = new List<IPhysicsObject>();
        }

        public virtual void Apply() {
            foreach (var physicElement in PhysicElements) {
                var pos = Apply(physicElement).Position;
                physicElement.Position = pos;
            }
            PhysicElements.Clear();
        }

        public virtual void AddPhysicElements(IEnumerable<IPhysicsObject> elements)
        {
            _physicElements.AddRange(elements.ToList());
        }

        public IPhysicsObject Apply(IPhysicsObject physicsObject)
        {
            var forceToApply = physicsObject.Direction;
            forceToApply = ApplyGravity(forceToApply);
            forceToApply = ApplyAirFriction(forceToApply);

            physicsObject.Direction = forceToApply*physicsObject.Speed;

            physicsObject.Position = ApplyForce(physicsObject.Position, physicsObject.Direction);
            return physicsObject;
        }

        private static Vector2 ApplyForce(Vector2 position, Vector2 forceToApply) {
            position.X = position.X - forceToApply.X;
            position.Y = position.Y - forceToApply.Y;

            return position;
        }

        private Vector2 ApplyAirFriction(Vector2 forceToApply) {
            return forceToApply.Reduce(_airResistance);
        }

        private Vector2 ApplyGravity(Vector2 forceToApply) {
            return forceToApply.Substract(_gravity);
        }
    }
}